#include "mainwindow.h"
#include "person.h"
#include "participant.h"
#include "recordedtime.h"
#include "ticksheet.h"
#include "participantsheet.h"

#include "ui_mainwindow.h"

#include <QDateTime>
#include <QTime>
#include <QTimer>
#include <QMessageBox>
#include <QTreeView>
#include <QStandardItemModel>
#include <QFileDialog>
#include <QTextStream>
#include <QSqlTableModel>
#include <QCompleter>
#include <QSqlQuery>
#include <QByteArray>
#include <QFile>
#include <QTextStream>
#include <QHeaderView>
#include <QModelIndex>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),
    ui(new Ui::MainWindowClass),
    mCurrentTicksheet(0),
    mCurrentParticipantSheet(0),
    mParticipantFormPosition(-1)

{
    ui->setupUi(this);

    //
    // Setup panel
    //

    ui->eventRaceDirector->setText("(race director)");

    //
    // Participants panel
    //
    
    enableNameAutoComplete(false);

    mCurrentParticipantSheet = new ParticipantSheet();

    mParticipantsModel = new QStandardItemModel(this);
    mParticaipantHeaderLabels << "Bib#" << "Name" << "Age" << "Sex" << "City";
    mParticipantsModel->setHorizontalHeaderLabels(mParticaipantHeaderLabels);

    ui->participantsTable->setModel(mParticipantsModel);
    ui->participantsTable->setAlternatingRowColors(true);
    ui->participantsTable->setRootIsDecorated(false);
    ui->participantsTable->setAlternatingRowColors(true);
    ui->participantsTable->setRootIsDecorated(false);
    ui->participantsTable->setSelectionMode(QAbstractItemView::ContiguousSelection);
    ui->participantsTable->header()->setResizeMode(QHeaderView::ResizeToContents);

    connect(ui->saveParticipantButton, SIGNAL(clicked()), SLOT(on_addParticipantButton_clicked()));
    connect(ui->editParticipantButton, SIGNAL(clicked()), SLOT(on_editParticipantButton_clicked()));

    //
    // Timer panel
    //

    ui->clockTimeLabel->setText(QString("00:00.00"));
    ui->startButton->setEnabled(true);
    ui->restartButton->setEnabled(false);
    ui->startedLabel->setText("Timer not started");
    //ui->startTimeLabel->setText(mElapsedTime->toString());
    ui->recordButton->setEnabled(false);
    ui->noteEdit->setEnabled(false);

    mRecordedTimesModel = new QStandardItemModel();
    QStringList headerLabels;
    headerLabels << "#" << "Elapsed time" << "Notes";
    mRecordedTimesModel->setHorizontalHeaderLabels(headerLabels);

    mTreeView = ui->treeView;
    mTreeView->setAlternatingRowColors(true);
    mTreeView->setRootIsDecorated(false);
    mTreeView->setModel(mRecordedTimesModel);
    mTreeView->setColumnWidth(0, 25);
    mTreeView->header()->setResizeMode(QHeaderView::ResizeToContents);

    ui->actionRecord->setEnabled(false);

    // Results panel

    mResultsModel = new QStandardItemModel();
    ui->resultsTree->setAlternatingRowColors(true);
    ui->resultsTree->setRootIsDecorated(false);
    ui->resultsTree->header()->setResizeMode(QHeaderView::ResizeToContents);
    mResultsHeaderLabels << "Place#" << "Elapsed time" << "Notes"
                         << "Bib#" << "Name" << "Age" << "Sex" << "City";

    mResultsModel->setHorizontalHeaderLabels(mResultsHeaderLabels);
    ui->resultsTree->setModel(mResultsModel);

    // connect signal/slots

    connect(mCurrentParticipantSheet, SIGNAL(participantsChanged()),
            SLOT(slot_participantlist_changed()));
    connect(mCurrentParticipantSheet, SIGNAL(participantsChanged()),
            SLOT(slot_buildResultslist()));
    makeDir();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::makeDir()
{
    QString dateString = QDateTime::currentDateTime().toString("yyyy-MM-dd");
    mRootDir = QDir::homePath();

    QString subDir;
    QTextStream(&subDir) << "Racetime/" << dateString;

    mRootDir.mkpath(subDir);
    mRootDir.cd(subDir);

    qDebug("Made working directory at '%s'", qPrintable( mRootDir.absolutePath() ));
}

void MainWindow::on_startButton_clicked()
{
    if (mCurrentTicksheet)
        delete mCurrentTicksheet;

    // Start a new timer session
    mCurrentTicksheet = new Ticksheet(0,ui->syncHours->value(),
                                        ui->syncMins->value(),
                                        ui->syncSecs->value());

    ui->startedLabel->setText("Clock start time:");
    ui->startTimeLabel->setText(mCurrentTicksheet->elapsedTimeString());

    ui->startButton->setEnabled(false);
    ui->restartButton->setEnabled(true);

    ui->recordButton->setEnabled(true);
    ui->noteEdit->setEnabled(true);

    ui->clockTimeLabel->setStyleSheet("background-color: rgba(207, 255, 203, 150);");
    ui->numberLabel->setText("1");
    ui->noteEdit->setFocus();
    ui->actionRecord->setEnabled(true);

    connect(mCurrentTicksheet, SIGNAL(timeChanged(const QString&)),
            SLOT(on_timerChanged(const QString&)));
    connect(mCurrentTicksheet, SIGNAL(timeRecorded()),
            SLOT(slot_buildResultslist()));
}

void MainWindow::on_restartButton_clicked()
{
    int ret = QMessageBox::warning(this, tr("RaceTime"),
                                    tr("This will reset the race time to 00:00.00!\n"
                                    "Are you sure you want to do this?"),
                                    QMessageBox::Cancel | QMessageBox::Yes);
    if (ret == QMessageBox::Yes)
    {
        on_actionQuickSave_triggered();

        mCurrentTicksheet->stop();

        //mElapsedTime->restart();

        ui->startButton->setEnabled(true);
        ui->restartButton->setEnabled(false);
        ui->clockTimeLabel->setStyleSheet("");

        ui->startedLabel->setText("Timer reset");
        ui->startTimeLabel->setText("");

        ui->recordButton->setEnabled(false);
        ui->noteEdit->setEnabled(false);
        ui->actionRecord->setEnabled(false);
    }
}

void MainWindow::on_recordButton_clicked()
{
    const QString note = ui->noteEdit->text();
    RecordedTime* recordedTime = mCurrentTicksheet->recordTime(note);

    //Update our timer list by inserting the new item
    QList<QStandardItem*> standardItemList = recordedTime->standardItemList();
    mRecordedTimesModel->appendRow(standardItemList);

    //Scroll down so we always see the new item
    mTreeView->scrollToBottom();

    //Reset the note line edit for the next entry
    ui->noteEdit->setText("");
    ui->noteEdit->setFocus();

    //Update the record number field
    ui->numberLabel->setText(QString().setNum(mRecordedTimesModel->rowCount()));
}

void MainWindow::on_timerChanged(const QString& elapsed )
{
    ui->clockTimeLabel->setText(elapsed);
}

void MainWindow::on_noteEdit_returnPressed()
{
    on_recordButton_clicked();
}

void MainWindow::on_noteEdit_textChanged ( const QString & text )
{
    QString uppertext = text.toUpper();
    ui->noteEdit->setText(uppertext);
}

void MainWindow::on_actionSave_triggered()
{
    QString fileName = QFileDialog::getSaveFileName(this);
    if (fileName.isEmpty())
        return;
    mCurrentTicksheet->saveFile(fileName);
}

void MainWindow::on_actionQuickSave_triggered()
{
    QString dateTimeString = QDateTime::currentDateTime().toString("hh-mm-ss-zzz");
    QString timerFile;
    QTextStream(&timerFile) << "timer-" << dateTimeString << ".txt";
    QString fileName = mRootDir.absoluteFilePath(timerFile);

    qDebug("Writing timer file: '%s'", qPrintable(fileName));

    mCurrentTicksheet->saveFile(fileName);
}

void MainWindow::on_actionToggleWarning_triggered()
{
    qDebug("Toggle Warning");
    mCurrentTicksheet->toggleLastTimeWarning();
}

void MainWindow::on_actionRecord_triggered()
{
    on_recordButton_clicked();
}



void MainWindow::on_importButton_clicked()
{
    importDatabase();
}

void MainWindow::importDatabase()
{
    printf("importDatabase()\n");

    // filename = "C:\\lmjsdb.txt";
    QString filename = QFileDialog::getOpenFileName(this,
            tr("Open database"),
            "C:\\", tr("Txt Files (*.txt)"));
    ui->dbinfoLabel->setText(filename);
    ui->importProgress->setEnabled(true);
    ui->importProgress->reset();

    QFile file(filename);
    if (!file.open(QFile::ReadOnly))
    {
        ui->statusBar->showMessage(file.errorString());
        return;
    }

    ui->dbinfoLabel->setText("File opened");
    QByteArray fp = file.readAll();
    qDebug("text length = %d", fp.size());
    QList<QByteArray> lines = fp.split('\r');

    QMap<QString, int> g;
    QList<QByteArray> headerList = lines[0].split('\t');

    for (int col=0; col < headerList.size(); col++)
    {
        QString headerLabel = headerList[col];
        g[headerLabel] = col;
    }

    QSqlQuery query;
    int i = 100;
    int totalLines = lines.size();

    qDebug("Total lines = %d", totalLines);

    QString info;
    QTextStream(&info) << "Read " << totalLines << " lines of file";
    ui->dbinfoLabel->setText(info);

    QString line;
    int linenumber = 0;
    QStringList keys;
    QStringList cities;
    foreach (line, lines)
    {
        ui->importProgress->setValue(int( linenumber/(float)totalLines * 100));
        if (linenumber > 2)
        {
            QStringList fields = line.split('\t');
            if (fields.size() == headerList.size())
            {
                // Primary members
                QString firstname = fields[g["First name"]];
                QString lastname = fields[g["Last name"]];
                QString ageStr = fields[g["Age-pm"]];
                int age = 0;
                if (ageStr.size() > 0)
                    age = ageStr.toInt();
                QString genderStr = fields[g["Sex-pm"]];
                Person::Gender gender;
                if (genderStr == "M")
                    gender = Person::Male;
                else
                    gender = Person::Female;
                QString city = fields[g["City"]];
                QString state = fields[g["State"]];

                Person* person = new Person(firstname, lastname, age, gender, city, state, true);
                mNameCompleteDatabase[person->key()] = person;
                keys.append(person->key());
                if (!cities.contains(city))
                    cities.append(city);
                i++;
            }
        }
        linenumber++;
    }


    ui->importProgress->reset();

    mNameCompleter = new QCompleter(keys, this);
    mNameCompleter->setCaseSensitivity(Qt::CaseInsensitive);
    ui->nameEdit->setCompleter(mNameCompleter);

    mCityCompleter = new QCompleter(cities, this);
    mCityCompleter->setCaseSensitivity(Qt::CaseInsensitive);
    ui->cityEntry->setCompleter(mCityCompleter);
    enableNameAutoComplete(true);
}

void MainWindow::enableNameAutoComplete(bool on)
{
    if (on)
    {
        ui->nameEdit->setEnabled(true);
        ui->databaseStatus->setText("Auto-complete on");
        ui->databaseStatus->setStyleSheet("background-color: rgb(181, 255, 160); border: 2px white; border-radius: 10px; border-style: outset; border-color: rgb(181, 255, 160);");
    } else {
        ui->nameEdit->setEnabled(false);
        ui->databaseStatus->setText("No DB loaded");
        ui->databaseStatus->setStyleSheet("background-color: rgb(255, 251, 114); border: 2px white; border-radius: 10px; border-style: outset; border-color: rgb(255, 251, 114);");        
    }
}

void MainWindow::on_nameEdit_returnPressed()
{
    qDebug("Return pressed on name edit\n");
    QString key = ui->nameEdit->text();
    Person* person = mNameCompleteDatabase[key];
    if (ui->autoIncrementBib->checkState() != Qt::Checked)
        ui->bibNumber->selectAll();

    // Make a new participant from the member database completion
    int bib = ui->bibNumber->text().toInt();
    bool member = true;
    Participant* participant = new Participant(bib, member, person);
    int pos = mCurrentParticipantSheet->nextParticipantPosition();
    editParticipantInForm(participant, pos, false);
}

void
MainWindow::editParticipantInForm(Participant* participant, int pos, bool edit)
{
    mParticipantFormPosition = pos;

    QString title;
    if (edit)
        QTextStream(&title) << "Editing position " << pos+1 << "";
    else
        QTextStream(&title) << "Appending new entry";

    ui->participantForm->setTitle(title);
    if (edit)
    {
        QString bibNumberString;
        bibNumberString.sprintf("%d", participant->bib());
        ui->bibNumber->setText(bibNumberString);
    }

    ui->nameEdit->setText(participant->name());
    ui->ageEntry->setValue(participant->age());
    ui->sexEntry->setCurrentIndex(participant->genderIndex());
    ui->cityEntry->setText(participant->origin());
    ui->bibNumber->setFocus();
}

Participant*
MainWindow::makeParticipantFromForm()
{
    //takes the form values and makes a new participant from that
    int bib = ui->bibNumber->text().toInt();
    QString name = ui->nameEdit->text();
    int age = ui->ageEntry->text().toInt();
    int genderIndex = ui->sexEntry->currentIndex();
    Person::Gender gender = (Person::Gender)genderIndex;
    QString origin = ui->cityEntry->text();
    bool member = true;
    qDebug("Add participant");
    Participant* participant = new Participant(bib, name, age, gender, origin, member);
    return participant;
}

void MainWindow::on_addParticipantButton_clicked()
{
    Participant* participant = makeParticipantFromForm();

    if (mParticipantFormPosition == mCurrentParticipantSheet->nextParticipantPosition())
        mCurrentParticipantSheet->addParticipant(participant);
    else
        mCurrentParticipantSheet->changeParticipant(mParticipantFormPosition, participant);

    ui->nameEdit->clear();

    clearParticipantForm();

    if (ui->nameEdit->isEnabled())
        ui->nameEdit->setFocus();
    else
        ui->bibNumber->setFocus();
}

void MainWindow::on_editParticipantButton_clicked()
{
    const QModelIndexList indexList = ui->participantsTable->selectionModel()->selectedRows(0);
    if (indexList.size() > 0)
    {
        int row = indexList.at(0).row();
        qDebug("Row = %d\n", row);
        Participant* participant = mCurrentParticipantSheet->getParticipant(row);
        if (participant)
            editParticipantInForm(participant, row, true);
    }
}


void MainWindow::on_removeParticipantButton_clicked()
{
    const QModelIndexList indexList = ui->participantsTable->selectionModel()->selectedRows(0);
    for (int i=0; i < indexList.size(); i++)
    {
        int row = indexList.at(i).row();
        mCurrentParticipantSheet->removeParticipant(row);
    }
}

void MainWindow::on_insertParticipantButton_clicked()
{
    const QModelIndexList indexList = ui->participantsTable->selectionModel()->selectedIndexes();
    int lowestRow = 99999;
    int row;
    for (int i=0; i < indexList.size(); i++)
    {
        int row = indexList.at(i).row();
        if (row < lowestRow)
            lowestRow = row;
    }
    if (lowestRow < 0)
        return;

    Participant* participant = makeParticipantFromForm();
    mCurrentParticipantSheet->insertParticipant(lowestRow, participant);

    ui->nameEdit->clear();
    clearParticipantForm();

    if (ui->nameEdit->isEnabled())
        ui->nameEdit->setFocus();
    else
        ui->bibNumber->setFocus();
}

void MainWindow::clearParticipantForm()
{
    QString title;
    QTextStream(&title) << "Appending new entry";
    ui->participantForm->setTitle(title);
    mParticipantFormPosition = mCurrentParticipantSheet->nextParticipantPosition();

    int bib = ui->bibNumber->text().toInt();
    int newBib = getNextBibNumber(bib);

    QString bibNumberString;
    bibNumberString.sprintf("%d", newBib);
    ui->bibNumber->setText(bibNumberString);
    ui->nameEdit->clear();
    ui->ageEntry->setValue(20);
    ui->sexEntry->setCurrentIndex(0);
    ui->cityEntry->clear();
}

void MainWindow::slot_participantlist_changed()
{
    qDebug("slot_participantlist_changed");
    mParticipantsModel->clear();
    mParticipantsModel->setHorizontalHeaderLabels(mParticaipantHeaderLabels);

    QList<Participant*> participants = mCurrentParticipantSheet->shownParticipants();
    qDebug("   participantlist count = %d", participants.size());
    for (int i = 0; i < participants.size(); i++)
    {
        Participant* participant = participants.at(i);

        QList<QStandardItem*> standardItemList = participant->standardItemList();
        mParticipantsModel->appendRow(standardItemList);
    }

    ui->participantsTable->scrollToBottom();

}

void MainWindow::on_moveUpButton_clicked()
{
    QList<int> rows;
    const QModelIndexList indexList = ui->participantsTable->selectionModel()->selectedRows(0);
    for (int i=0; i < indexList.size(); i++)
    {
        int row = indexList.at(i).row();
        rows.append(row);
    }
    qSort(rows.begin(), rows.end());
    for (int i=0; i < rows.size(); i++)
        mCurrentParticipantSheet->moveParticipantUp(rows[i]);
}

void MainWindow::on_moveDownButton_clicked()
{
    QList<int> rows;
    const QModelIndexList indexList = ui->participantsTable->selectionModel()->selectedRows(0);
    for (int i=0; i < indexList.size(); i++)
    {
        int row = indexList.at(i).row();
        rows.append(row);
    }
    qSort(rows.begin(), rows.end(), qGreater<int>());
    for (int i=0; i < rows.size(); i++)
        mCurrentParticipantSheet->moveParticipantDown(rows[i]);
}

int MainWindow::getNextBibNumber(int oldBibNumber)
{
    int bib = oldBibNumber;
    if (ui->autoIncrementBib->checkState() == Qt::Checked)
    {
        Participant* participant = mCurrentParticipantSheet->getLastParticipant();
        if (participant)
            bib = participant->bib() + 1;
        else
            bib = oldBibNumber + 1;
    }

    return bib;
}

void MainWindow::slot_buildResultslist()
{
    //
    // Combine the timing list with the participants list
    //
    if (!mCurrentTicksheet)
        return;

    mResultsModel->clear();
    mResultsModel->setHorizontalHeaderLabels(mResultsHeaderLabels);

    QList<Participant*> participants = mCurrentParticipantSheet->shownParticipants();
    int numberOfParticipants = participants.count();

    QList<RecordedTime*> recordedTimes = mCurrentTicksheet->shownRecordedTimes();
    for (int i = 0; i < recordedTimes.size(); i++)
    {
        RecordedTime* recordedTime = recordedTimes.at(i);
        QList<QStandardItem*> standardItemList = recordedTime->standardItemList();

        if (i < numberOfParticipants) {
            Participant* participant = participants.at(i);
            QList<QStandardItem*> partStandardItemList = participant->standardItemList();
            for (int j = 0; j < partStandardItemList.size(); j++)
                standardItemList.append(partStandardItemList.at(j));
        }
        mResultsModel->appendRow(standardItemList);
    }

}
